package ru.apache_maven;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.ListIterator;

public class Money {
	private static int NOMINAL_COUNT_MAX = 200;  
	private static int NOMINAL_NUMBER_MAX = 10;
	private static int AMOUNT_MAX = 1000; //TODO переписать без рекурсии и можно будет увеличить
	private ArrayList<Integer> wallet;
	private ArrayList<Integer> bestWallet;
	private ArrayList<Integer> nominals;
	private int amount;
	private int best;
	
	public Money() {
		nominals = new ArrayList<Integer>();
		wallet = new ArrayList<Integer>();
		amount = 0;
	}
	
	/**
	 * Функция возвращает сдачу
	 * @return wallet ArrayList<Integer> - набор монет, т.е. сдача
	 */
	public ArrayList<Integer> getChange()
	{
		best = amount;
		wallet = calculate(amount, wallet, 0);
		printResult(wallet);
		return wallet;
	}
	
	public void input() {
		System.out.println("Введите количество номиналов в диапазоне [1, " + NOMINAL_NUMBER_MAX + "]");
		int nominalNumber = 0;
		try {
			nominalNumber = inputRead(NOMINAL_NUMBER_MAX); // Запрос на ввод количества номиналов
			for (int i=0; i<nominalNumber; i++) {
				Integer inp;
				System.out.print("Введите номинал " + (i+1) + "-ой монеты в диапазоне [1, " + NOMINAL_COUNT_MAX + "]");
				inp = inputRead(NOMINAL_COUNT_MAX); // Запрос на ввод номинала 
				nominals.add(inp);
			}
			System.out.println("Введите сумму для размена в диапазоне [1, " + AMOUNT_MAX + "]");
			amount =inputRead(AMOUNT_MAX); // Запрос на ввод суммы для размена
		} catch (InputException e) {
			e.printStackTrace();
		} // Запрос на ввод количества номиналов
	}
	
	/**
	 * Метод выводит в консоль сколько каких монет нужно для размена
	 * Такой метод не ищет оптимальный набор, но является быстровыполнимым
	 * @param sum int - сумма для размена
	 * @return wallet ArrayList<Integer> - кошелек
	 */
	private void printResult(ArrayList<Integer> res) {
		System.out.println("Для выдачи такой суммы понадобиться: ");
		for(ListIterator<Integer> i = nominals.listIterator(); i.hasNext(); ) {
			Integer currentMonet = i.next();
			System.out.println("Монет номиналом " + currentMonet + " - " +  wallet.get(i.previousIndex()) + "шт.");
		}
		if (best>0){
			System.out.println("Невыдаваемый остаток " + best);
		}
	}
	
	/**
	 * Часть рекурсивной функции. переписать
	 * @param sum
	 * @param wallet
	 * @param position
	 * @return
	 */
	private ArrayList<Integer> calculate(int sum, ArrayList<Integer> wallet, int position) {
		Collections.sort(nominals);
		Collections.reverse(nominals);
		//System.out.println("Для выдачи такой суммы понадобиться: ");
		//ArrayList<Integer> wallet = new ArrayList<Integer>();
		for(ListIterator<Integer> i = nominals.listIterator(position); i.hasNext(); ) {
			Integer currentMonet = i.next();
			int numbersOfMonet = (sum / currentMonet);
			int tempPosition = i.previousIndex();
			if (position != 0){
				wallet.set(tempPosition, numbersOfMonet);
			}
			else {
				wallet.add(numbersOfMonet);
			}
			/*if (numbersOfMonet > 0) {
				System.out.println("Монет номиналом " + currentMonet + " - " +  numbersOfMonet + "шт.");
			}*/
			sum -= currentMonet * numbersOfMonet;
		}
		if (sum == 0) {
			bestWallet = new ArrayList<Integer>();
			for(ListIterator<Integer> i = wallet.listIterator(); i.hasNext(); ) {
				bestWallet.add((int)i.next());
			}
			best = sum;
			System.out.println("Размен прошел успешно!");
			return bestWallet;
		}
		else {
			if (sum < best) {
				bestWallet = new ArrayList<Integer>();
				for(ListIterator<Integer> i = wallet.listIterator(); i.hasNext(); ) {
					bestWallet.add((int)i.next());
				}
				best = sum;
			}
			calculate2(sum, wallet);
		}
		return bestWallet;
	}
	
	/**
	 * Вторая часть рекурсивной функции. переписать без рекурсии
	 * @param sum
	 * @param inWallet
	 * @return
	 */
	private ArrayList<Integer> calculate2(int sum, ArrayList<Integer>  inWallet)
	{
		ArrayList<Integer> walletOut = new ArrayList<Integer>();
		boolean firstPoint = true;
		for (int i=inWallet.size() - 1; i>=0; --i){
			if (inWallet.get(i) == 0 ) {
				firstPoint = false;
			}
			else if(firstPoint) {
				sum = sum + nominals.get(i) * inWallet.get(i);
				inWallet.set(i, 0);
				firstPoint = false;
			}
			else if(!firstPoint) {
				sum = sum + nominals.get(i);
				inWallet.set(i, inWallet.get(i) - 1);
				walletOut = calculate(sum, inWallet, i+1);
				break;
			}
			/*if (i==0) {
				//System.out.println("Error");//дописать
			}*/
		}
		return walletOut;
	}
	
	/**
	 * Функция считывает введеные значения с клавиатуры
	 * @param maxValue int - придел диапазона
	 * @return readedNumber Integer - введеное число
	 * @throws InputException - исключение вызываемое, когда вводимое число вне заданного диапазона
	 */
	private Integer inputRead(int maxValue) throws InputException {
		InputStreamReader isr = new InputStreamReader(System.in);
		BufferedReader br = new BufferedReader(isr);
		Integer readedNumber = 0;
		System.out.println("");
		Boolean isCorrect = false; 
		while(!isCorrect)
		{
			try {
				String str = br.readLine();
				readedNumber = Integer.parseInt(str);
				if(readedNumber <= 0 || readedNumber > maxValue) {
					throw new InputException(readedNumber);
					}
				isCorrect = true;
			} catch (InputException e) {
				System.out.println("Соблюдайте диапазон [1, " + maxValue + "]");
			} catch (Exception e) {
				System.out.println("Введите число в диапазоне [1, " + maxValue + "]");
			}
		}
		return readedNumber;
	}

	public final ArrayList<Integer> getNominals() {
		return nominals;
	}

	public final void setNominals(ArrayList<Integer> nominals) {
		this.nominals = nominals;
	}

	public final int getAmount() {
		return amount;
	}

	public final void setAmount(int amount) {
		this.amount = amount;
	}
}
